﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;

using DocumentFormat.OpenXml.Wordprocessing;

using ProjectBase.Core;

namespace $safeprojectname$.Export.Word
{
    public class DocumentWalker
    {
        public virtual void Execute(IEnumerable<DocPart> parts, IPersistentObject entity)
        {
            // nothing to process
            if (!parts.IsNotEmpty())
            {
                return;
            }

            // handlers
            var handlers = Core.Service.Factory.CreateInstances<IDocPartHandler>()
                .OrderBy(h => h.Priority)
                .ToArray();

            Handler = handlers.FirstOrDefault();
            for (int i = 0; i < handlers.Length - 1; i++)
            {
                handlers[i].SetSuccessor(handlers[i + 1]);
            }

            // is needed for inner/deeper diving
            AllParts = parts;

            // start at the root level
            foreach (var part in parts.Where(b => !b.Ancestors<DocPart>() // iterates all root DocParts
                                                       .Any(parts.Contains))) // (parentless)
            {
                ProcessEntity(part, entity);
            }
        }

        protected virtual void ProcessEntity(DocPart part, IPersistentObject entity, params string[] prefixes)
        {
            if (Handler.Is()
              && Handler.Handle(part, entity, prefixes).Equals(HandlerResult.Finished))
            {
                return;
            }
            if (ProcessPersistent(part, entity, prefixes))
            {
                return;
            }
            if (ProcessEntities(part, entity, prefixes))
            {
                return;
            }

            part.Remove();
        }

        #region Process persistent
        protected virtual bool ProcessPersistent(DocPart part, IPersistentObject entity, params string[] prefixes)
        {
            var name = part.Name(prefixes);

            // Entity as inner property
            IPersistentObject persistent;
            if (entity.TryGetResult(name, out persistent))
            {
                return InjectIPersistent(part, prefixes, name, persistent);
            }
            return false;
        }

        protected virtual bool ProcessEntities(DocPart part, IPersistentObject entity, params string[] prefixes)
        {
            var name = part.Name(prefixes);

            // Collection of inner Entities
            IEnumerable<IPersistentObject> persistents;
            if (entity.TryGetResult(name, out persistents)) // there is a list of Entities
            {
                foreach (var item in persistents)
                {
                    ProcessEntityTree(part, prefixes, name, item);
                }

                part.Remove();
                return true;
            }
            return false;
        }

        protected virtual void ProcessEntityTree(DocPart part, string[] prefixes, string name, IPersistentObject item)
        {
            if (item.IsNull())
            {
                return;
            }

            var newPart = part.InsertBeforeSelf(part.CloneNode(true)) as DocPart;

            var parts = newPart
                .Descendants<DocPart>()
                .Where(d => d.Ancestors<DocPart>().First().Equals(newPart))
                .ToList();

            foreach (var child in parts)
            {
                ProcessEntity(child, item, (new List<string>(prefixes) { name }.ToArray()));
            }

            newPart.RestoreElements();
        }

        #endregion Process persistent

        #region process

        protected virtual bool InjectIPersistent(DocPart part, string[] prefixes, string name, IPersistentObject persistent)
        {
            Contract.Requires(part.Is());
            Contract.Requires(persistent.Is());
            Contract.Requires(name.IsNotEmpty());

            if (part.Descendants<DocPart>()
                .Any(p => AllParts
                              .Any(ap => ap.Name().Equals(p.Name())
                                         && ap.InnerXml.Equals(p.InnerXml))))
            {
                ProcessEntityTree(part, prefixes, name, persistent);
                part.Remove();
                return true;
            }

            part.InjectValue(persistent.ToDisplay());
            return true;
        }
        #endregion process

        #region properties
        protected virtual IDocPartHandler Handler { get; set; }
        protected virtual IEnumerable<DocPart> AllParts { get; set; }
        #endregion properties
    }
}